package utils

import (
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"
	"math"
	"strconv"
	"strings"
)

type (
	// ShareFeeItem 计算分摊金额属性
	ShareFeeItem struct {
		Total    float64 // 计算分摊总额，例如：优惠券抵扣金额
		TotalFee float64 // 计算比例总额，例如：商品总额
		UnitFee  float64 // 单价：商品单价
	}
	// AgeItem 计算年龄
	AgeItem struct {
		Birthday string // 生日
		Type     int    // 类型：0默认年岁，1年月，2年月日, 3年月日时，4年月日时分
		IsFmt    bool   // 是否格式年月日：false默认格式化，true不格式化返回（岁数，不返月份和天数）
	}
)

// ShareFeeOrRate 计算分摊金额和比例
func ShareFeeOrRate(item ShareFeeItem, addShareFee, addRate *float64) (shareFee, rate float64) {
	// 累计分摊金额大于计算分摊总额或分摊比例大于1-解引用
	if *addShareFee >= item.Total || *addRate >= 1 {
		return 0, 0
	}

	// 计算分摊比例和金额
	rate = Div(item.UnitFee, item.TotalFee, 3)
	shareFee = Mul(rate, item.Total, 2)

	// 确保分摊累计不能超过1：保留两位数精度
	if *addRate+rate >= 0.99 {
		shareFee = Scale(item.Total-*addShareFee, 2)
		rate = Scale(1-*addRate, 3)
	}

	// 累计分摊金额和比例-引用传递
	*addShareFee += shareFee
	*addRate += rate

	return shareFee, rate
}

// Age 计算年龄
func Age(item AgeItem) (age uint, fmAge string) {
	// 格式化时间
	var (
		birthday int64
		ageArr   []string
		dUnit    float64 = 86400
		units            = []float64{365.25 * dUnit, 30.4375 * dUnit, dUnit, 3600, 60}
	)
	switch gstr.IsNumeric(item.Birthday) {
	case false:
		// 格式化时间
		birthday = gtime.New(item.Birthday).Timestamp()
	default:
		// 默认时间戳
		birthday = gconv.Int64(item.Birthday)
	}

	// 获取当前时间-出生年龄，获得时间戳
	dayTime := float64(gtime.Timestamp() - birthday)
	if dayTime < dUnit && item.Type < 3 {
		return 0, ""
	}

	// 处理计算实际年龄
	for key, u := range units {
		// 计算年龄
		tempAge := math.Floor(dayTime / u)
		// 计算出年龄不在计算月日
		if age == 0 {
			age = uint(tempAge)
			// 不允许格式化，只能返回年龄（一年以内直接返回0，若要计算虚岁，则向上取一）
			if item.IsFmt == true {
				return age, ""
			}
		}

		// 指定类型计算设定类型，截断后续操作
		if key == item.Type {
			break
		}
		// 计算出当前等于0，不操作
		if tempAge == 0 {
			continue
		}

		fm := strconv.Itoa(int(tempAge))
		// 递减去当前等级的时间戳
		dayTime -= tempAge * u
		// 拼接时间单位
		switch key {
		case 0:
			fm += "岁"
		case 1:
			fm += "月"
		case 2:
			fm += "天"
		case 3:
			fm += "小时"
		case 4:
			fm += "分"
		default:
		}
		ageArr = append(ageArr, fm)
	}

	// 拼接并格式化时间
	return age, strings.Join(ageArr, "")
}

// Add 加法,两数及以上的数值累计相加之和，并保留小数点后两位
// Example:
// Add(100, 2000, 2, 100.01111)
// Output: 2200.01
func Add(x, y interface{}, decimal float64, z ...interface{}) float64 {
	sum := Float64(x) + Float64(y)
	if len(z) > 0 {
		for _, v := range z {
			sum += Float64(v)
		}
	}
	return Scale(sum, decimal)
}

// Sub 减法,两数及以上的数值累计之差，并保留小数点后两位
// Example:
// Sub(100, 2000, 2, 100.01111)
// Output: -2000.01
func Sub(x, y interface{}, decimal float64, z ...interface{}) float64 {
	s := Float64(x) - Float64(y)
	if len(z) > 0 {
		for _, v := range z {
			s -= Float64(v)
		}
	}
	return Scale(s, decimal)
}

// Mul 乘法,两数及以上的数值乘积，并保留小数点后两位
// Example:
// Mul(100, 2000, 2, 100.01111)
// Output: 2.0002222e+07
func Mul(x, y interface{}, decimal float64, z ...interface{}) float64 {
	s := Float64(x) * Float64(y)
	if len(z) > 0 {
		for _, v := range z {
			s *= Float64(v)
		}
	}
	if s == 0 {
		return 0
	}
	return Scale(s, decimal)
}

// Div 除法， 两数相除
// Example:
// Sub(999, 8989.5645, 2)
// Output: 0.11
func Div(x, y interface{}, decimal float64) float64 {
	if Float64(y) == 0 || Float64(x) == 0 {
		return 0
	}
	s := Float64(x) / Float64(y)
	return Scale(s, decimal)
}

// Scale 保留小数点-四舍五入算法
// Example:
// Scale(98.88888, 2) 保留两位小数
// Output: 98.89
func Scale(s, decimal float64) float64 {
	// 保留小数点，注：避免小数点小于等于0，计算错误
	var d float64 = 1
	if decimal > 1 {
		d = math.Pow(10, decimal) // 10的N次方
	}
	return math.Round(s*d) / d
}
